package com.bestcem.xm.member.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.common.core.uitls.StringUtil;
import com.bestcem.xm.label.entity.pojo.LabelDO;
import com.bestcem.xm.label.service.LabelService;
import com.bestcem.xm.member.constant.Constants;
import com.bestcem.xm.member.constant.FieldDisplayNameConstant;
import com.bestcem.xm.member.controller.vo.ExtendFieldVO;
import com.bestcem.xm.member.dao.ExtendFieldDao;
import com.bestcem.xm.member.dao.ExtendFieldEnumItemDao;
import com.bestcem.xm.member.entity.mongo.ExtendField;
import com.bestcem.xm.member.entity.pojo.ExtendFieldDO;
import com.bestcem.xm.member.entity.pojo.ExtendFieldEnumItemDO;
import com.bestcem.xm.member.enums.ExtendFieldTypeEnum;
import com.bestcem.xm.member.enums.MemberTypeEnum;
import com.bestcem.xm.member.grpc.service.param.extendfield.*;
import com.bestcem.xm.member.service.ConvertUtil;
import com.bestcem.xm.member.service.ExtendFieldService;
import com.bestcem.xm.member.service.MemberService;
import com.bestcem.xm.member.service.ProjectService;
import com.bestcem.xm.member.service.dto.ExtendFieldDTO;
import com.bestcem.xm.member.service.dto.ExtendFieldEnumItemDto;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 扩展字段业务层实现类
 * @author dc.wang <dc.wang@idiaoyan.com>
 * @version V1.0
 * @date 2021/7/20
 */
@Service
public class ExtendFieldServiceImpl implements ExtendFieldService {

    private final ExtendFieldDao extendFieldDao;

    private final ExtendFieldEnumItemDao extendFieldEnumItemDao;

    private final ProjectService projectService;

    private final MemberService memberService;

    private final LabelService labelService;

    @Autowired
    public ExtendFieldServiceImpl(ExtendFieldDao extendFieldDao,
                                  ExtendFieldEnumItemDao extendFieldEnumItemDao,
                                  ProjectService projectService,
                                  MemberService memberService,
                                  LabelService labelService) {
        this.extendFieldDao = extendFieldDao;
        this.extendFieldEnumItemDao = extendFieldEnumItemDao;
        this.projectService = projectService;
        this.memberService = memberService;
        this.labelService = labelService;
    }

    @Override
    public ServiceResult<List<ExtendFieldEnumItemDto>> listDisplayFieldEnumItems(ListDisplayFieldEnumItemsParam param) {
        boolean isDisplay = !Objects.equals(param.getDisplayStatus(), 0);
        List<ExtendFieldEnumItemDO> enumItems = extendFieldEnumItemDao
                .findByFieldIds(Arrays.asList(param.getFieldId()), isDisplay);

        List<ExtendFieldEnumItemDto> enumItemDTOs = enumItems.stream().map(this::convertFrom)
                .collect(Collectors.toList());
        return ServiceResult.success(enumItemDTOs);
    }

    @Override
    public ServiceResult<List<ExtendFieldDTO>> batchCreateFields(BatchInsertFieldsParam param) {
        List<InsertFieldParam> insertFieldParams = param.getInsertFieldParams();

        // 校验非法字段名
        String illegalNameMsg = this.checkIllegalFieldNames(param.getOrgId(), insertFieldParams);
        if (!StringUtils.isBlank(illegalNameMsg)) {
            return ServiceResult.fail(illegalNameMsg);
        }

        List<ExtendFieldEnumItemDO> enumItems = new LinkedList<>();
        this.insertFields(param.getOrgId(), insertFieldParams, enumItems);

        if (!CollectionUtils.isEmpty(enumItems)) {
            extendFieldEnumItemDao.batchInsert(enumItems);
        }
        return ServiceResult.success();
    }

    @Override
    public ServiceResult<List<ExtendFieldDTO>> batchUpdatedFields(BatchUpdateFieldsParam param) {
        // 参数校验
        List<InsertFieldParam> insertFieldParams = Optional.ofNullable(param.getInsertFieldParams()).orElse(Collections.emptyList());
        List<UpdateFieldParam> updateFieldParams = Optional.ofNullable(param.getUpdateFieldParams()).orElse(Collections.emptyList());
        List<String> deleteFieldIds = Optional.ofNullable(param.getDeleteFieldIds()).orElse(Collections.emptyList());

        // 校验非法字段名
        String illegalNameMsg = this.checkIllegalFieldNames(param.getOrgId(), insertFieldParams, updateFieldParams, deleteFieldIds);
        if (!StringUtils.isBlank(illegalNameMsg)) {
            return ServiceResult.fail(illegalNameMsg);
        }

        // 校验最大限制数量
        boolean isIllegalFieldAmount = this.validateIllegalFieldAmount(param.getOrgId(), param.getMemberTypeEnum().getIndex(), param.getInsertFieldParams(), param.getDeleteFieldIds());
        if(isIllegalFieldAmount){
            return ServiceResult.fail(String.format("扩展字段数量不能超过%s个", Constants.MAX_EXTEND_FIELD_AMOUNT));
        }

        // fixme 前端传3代表项目
        if(MemberTypeEnum.HIDDEN.equals(param.getMemberTypeEnum())){
            // 修改项目扩展字段信息
            projectService.batchUpdateExtendField(param);
        }else{
            // 修改联系人扩展字段信息
            memberService.batchUpdateExtendField(param);
        }

        // 删除扩展字段
        List<String> deleteItemIds = new LinkedList<>();
        if(!CollectionUtils.isEmpty(param.getDeleteFieldIds())){
            this.deleteFields(param.getOrgId(), param.getDeleteFieldIds(), deleteItemIds);
        }

        // 创建扩展字段，返回枚举选项
        List<ExtendFieldEnumItemDO> allInsertEnumItems = new LinkedList<>();
        if(!CollectionUtils.isEmpty(insertFieldParams)){
            this.insertFields(param.getOrgId(), insertFieldParams, allInsertEnumItems);
        }

        // 更新扩展字段
        List<ExtendFieldEnumItemDO> updateEnumItems = new LinkedList<>();
        if(!CollectionUtils.isEmpty(updateFieldParams)){
            this.updateFields(param.getOrgId(), updateFieldParams, allInsertEnumItems, updateEnumItems, deleteItemIds);
        }

        // 创建、修改、删除枚举选项
        this.batchModifyEnumItems(param.getOrgId(), allInsertEnumItems, updateEnumItems, deleteItemIds);

        return ServiceResult.success();
    }

    /**
     * 校验扩展字段数量限制
     * @param orgId 企业标识
     * @param memberType 联系人类型
     * @param insertFieldParams 待插入扩展字段
     * @param deleteFieldIds 待删除扩展字段
     * @return true 校验失败，超过最大限制数量
     */
    private boolean validateIllegalFieldAmount(@NotNull String orgId,@NotNull Integer memberType, Collection<InsertFieldParam> insertFieldParams, Collection<String> deleteFieldIds){
        // 校验联系人最大扩展数量
        if(CollectionUtils.isEmpty(insertFieldParams)){
            return false;
        }

        int existDeleteAmount = 0;
        if(!CollectionUtils.isEmpty(deleteFieldIds)){
            List<ExtendFieldDO> deleteFields = extendFieldDao.findByIds(deleteFieldIds);
            existDeleteAmount = deleteFields.size();
        }

        int currentAmount = 0;
        if(!CollectionUtils.isEmpty(insertFieldParams)){
            List<ExtendFieldDO> currentFields = extendFieldDao.listMemberExtendField(orgId, memberType);
            currentAmount = currentFields.size();
        }

        return Constants.MAX_EXTEND_FIELD_AMOUNT - currentAmount - insertFieldParams.size() + existDeleteAmount < 0;
    }

    @Override
    public ServiceResult<List<ExtendFieldVO>> getFields(Integer attribute, String orgId) {
        List<ExtendFieldDO> extendFields = extendFieldDao.listMemberExtendField(orgId, attribute);
        if (CollUtil.isNotEmpty(extendFields)) {
            List<ExtendFieldVO> extendFieldVOS = extendFields.stream().map(extendField -> {
                ExtendFieldVO vo = ConvertUtil.extendFieldDO2ExtendFieldVO(extendField);
                return vo;
            }).collect(Collectors.toList());
            return ServiceResult.success(extendFieldVOS);
        }
        List<ExtendFieldVO> extendFieldVOS = new ArrayList<>();
        return ServiceResult.success(extendFieldVOS);
    }

    private String checkIllegalFieldNames(String orgId,
                                          @NotNull Collection<InsertFieldParam> insertFieldParams) {
        return this.checkIllegalFieldNames(orgId, insertFieldParams, new ArrayList<>(), new ArrayList<>());
    }

    private String checkIllegalFieldNames(String orgId,
                                          @NotNull Collection<InsertFieldParam> insertFieldParams,
                                          @NotNull Collection<UpdateFieldParam> updateFieldParams,
                                          @NotNull Collection<String> deleteFieldIds) {

        if(CollectionUtils.isEmpty(insertFieldParams) && CollectionUtils.isEmpty(updateFieldParams)){
            return "";
        }

        Set<String> allDistinctNames = new HashSet<>(insertFieldParams.size() + updateFieldParams.size());
        Map<String, String> nameAndIdMap = new HashMap<>(updateFieldParams.size());
        Set<MemberTypeEnum> memberTypeEnums = new HashSet<>();


        // 校验字段是否重名
        for (InsertFieldParam p : insertFieldParams) {
            allDistinctNames.add(p.getName());
            memberTypeEnums.add(p.getMemberTypeEnum());
            nameAndIdMap.put(p.getName(),"");
        }

        for (UpdateFieldParam p : updateFieldParams) {
            allDistinctNames.add(p.getName());
            memberTypeEnums.add(p.getMemberTypeEnum());
            nameAndIdMap.put(p.getName(), p.getId());
        }

        // 参数重名检测
        if (updateFieldParams.size() + insertFieldParams.size() > allDistinctNames.size()) {
            return "存在同名字段";
        }

        // 类型检测
        MemberTypeEnum memberTypeEnum = memberTypeEnums.stream().filter(Objects::nonNull).findFirst().orElse(null);
        if (memberTypeEnums.size() != 1 || memberTypeEnum == null) {
            return "联系人类型异常";
        }

        // 固定字段名
        Set<String> fixedFieldNames = FieldDisplayNameConstant.getMemberFixFieldNames(memberTypeEnum);

        // 计算字段名
        Set<String> computeFieldNames = FieldDisplayNameConstant.getComputeFixFieldNames();

        // 查询已存在扩展字段
        List<ExtendFieldDO> extendFields = extendFieldDao.findByNames(orgId, memberTypeEnum.getIndex(), nameAndIdMap.keySet());
        Map<String, ExtendFieldDO> nameFieldMap = new HashMap<>(extendFields.size());
        for(ExtendFieldDO extendFieldDO : extendFields){
            nameFieldMap.put(extendFieldDO.getName(), extendFieldDO);
        }

        // 查询标签名
        Set<String> labelNames = new HashSet<>();
        if (MemberTypeEnum.INDIVIDUAL.equals(memberTypeEnum)) {
            // 如果是个人联系人 获取标签信息
            List<LabelDO> labels = labelService.findByAttributeAndOrgId(orgId);
            labelNames = labels.stream().map(LabelDO::getName).collect(Collectors.toSet());
        }

        for (Map.Entry<String, String> entry : nameAndIdMap.entrySet()) {
            if (fixedFieldNames.contains(entry.getKey())) {
                return "字段名与固定字段名重复";
            }
            if (labelNames.contains(entry.getKey())) {
                return "字段名与标签名重复";
            }
            if (computeFieldNames.contains(entry.getKey())) {
                return "字段名与计算值重复";
            }
            ExtendFieldDO commonNameField = nameFieldMap.get(entry.getKey());
            if(commonNameField != null && !commonNameField.getFieldId().equals(entry.getValue()) && deleteFieldIds.contains(entry.getValue())){
                return "字段名重复";
            }
        }

        return "";
    }

    private Collection<ExtendFieldDO> updateFields(String orgId,
                                                   @NotNull Collection<UpdateFieldParam> updateFieldParams,
                                                   @NotNull Collection<ExtendFieldEnumItemDO> createEnumItems,
                                                   @NotNull Collection<ExtendFieldEnumItemDO> updateEnumItems,
                                                   @NotNull Collection<String> deleteEnumItemIds) {

        List<ExtendFieldDO> updateExtendFields = new ArrayList<>(updateFieldParams.size());
        Date now = DateUtil.getCommonDate();
        for (UpdateFieldParam updateFieldParam : updateFieldParams) {
            ExtendFieldDO extendField = this.convertFrom(updateFieldParam);
            extendField.setOrgId(orgId);
            extendField.setUpdateTime(now);
            updateExtendFields.add(extendField);

            if (!ExtendFieldTypeEnum.ENUM.equals(updateFieldParam.getExtendFieldTypeEnum())) {
                continue;
            }
            // 待创建枚举选项
            if (!CollectionUtils.isEmpty(updateFieldParam.getInsertEnumItemParams())) {
                for(InsertEnumItemParam e : updateFieldParam.getInsertEnumItemParams()){
                    ExtendFieldEnumItemDO enumItem = this.convertFrom(e);
                    enumItem.setDeleteStatus(1);
                    enumItem.setExtendFieldId(updateFieldParam.getId());
                    enumItem.setCreateTime(now);
                    enumItem.setUpdateTime(now);
                    createEnumItems.add(enumItem);
                }
            }
            // 待更新枚举选项
            if (!CollectionUtils.isEmpty(updateFieldParam.getUpdateEnumItemParams())) {
                for(UpdateEnumItemParam e : updateFieldParam.getUpdateEnumItemParams()){
                    ExtendFieldEnumItemDO enumItem = this.convertFrom(e);
                    enumItem.setExtendFieldId(updateFieldParam.getId());
                    enumItem.setUpdateTime(now);
                    updateEnumItems.add(enumItem);
                }
            }
            // 待删除枚举选项
            if (!CollectionUtils.isEmpty(updateFieldParam.getDeleteEnumItemIds())) {
                deleteEnumItemIds.addAll(updateFieldParam.getDeleteEnumItemIds());
            }
        }

        // 批量更新扩展字段信息
        if (!CollectionUtils.isEmpty(updateExtendFields)) {
            extendFieldDao.batchUpdateFields(updateExtendFields);
            // 循环更新联系人扩展字段
        }
        return updateExtendFields;
    }

    private Collection<ExtendFieldDO> insertFields(String orgId,
                                                   @NotNull Collection<InsertFieldParam> insertFieldParams,
                                                   @NotNull Collection<ExtendFieldEnumItemDO> insertEnumItems) {


        List<ExtendFieldDO> createFields = new ArrayList<>(insertEnumItems.size());
        Date now = DateUtil.getCommonDate();
        List<String> fieldIds = new ArrayList<>();
        for (InsertFieldParam f : insertFieldParams){
            ExtendFieldDO extendField = this.convertFrom(f);
            String fieldId = StringUtil.getId();
            extendField.setFieldId(fieldId);
            fieldIds.add(fieldId);
            extendField.setOrgId(orgId);
            extendField.setCreateTime(now);
            extendField.setUpdateTime(now);
            createFields.add(extendField);
        }
        extendFieldDao.batchInsertField(createFields);
        List<ExtendFieldDO> createdFields = extendFieldDao.findByIds(fieldIds);

        // 获取所有待创建枚举选项
        Map<String, String> nameAndFieldIdMap = createdFields.stream()
                .filter(createdField -> ExtendFieldTypeEnum.ENUM.matches(createdField.getType()))
                .collect(Collectors.toMap(ExtendFieldDO::getName, ExtendFieldDO::getFieldId));
        if (CollectionUtils.isEmpty(nameAndFieldIdMap)) {
            return new ArrayList<>(0);
        }

        // 关联扩展字段与枚举选项
        for (InsertFieldParam fieldParam : insertFieldParams) {
            if (!ExtendFieldTypeEnum.ENUM.equals(fieldParam.getExtendFieldTypeEnum()) || CollectionUtils.isEmpty(fieldParam.getInsertEnumItemParams())) {
                continue;
            }
            String fieldId = nameAndFieldIdMap.get(fieldParam.getName());
            for (InsertEnumItemParam enumItemParam : fieldParam.getInsertEnumItemParams()) {
                ExtendFieldEnumItemDO enumItem = this.convertFrom(enumItemParam);
                enumItem.setExtendFieldId(fieldId);
                enumItem.setCreateTime(now);
                enumItem.setUpdateTime(now);
                insertEnumItems.add(enumItem);
            }
        }
        return createdFields;
    }

    private void deleteFields(@NotNull String orgId,
                              @NotNull Collection<String> deleteFieldIds,
                              @NotNull Collection<String> deleteEnumItemIds){

        Integer deleteCount = extendFieldDao.batchDeleteFieldByIds(orgId, deleteFieldIds);

        if(deleteCount == null){
            // throw exception
        }

        // 查询字段对应的枚举选项
        Collection<ExtendFieldEnumItemDO> enumItems = extendFieldEnumItemDao.findByFieldIds(deleteFieldIds);
        if(!CollectionUtils.isEmpty(enumItems)){
            Collection<String> enumItemIds = enumItems.stream().map(ExtendFieldEnumItemDO::getFieldEnumId).collect(Collectors.toList());
            deleteEnumItemIds.addAll(enumItemIds);
        }
    }

    private void batchModifyEnumItems(String orgId,
                                      @NotNull Collection<ExtendFieldEnumItemDO> createEnumItems,
                                      @NotNull Collection<ExtendFieldEnumItemDO> updateEnumItems,
                                      @NotNull Collection<String> deleteEnumItemIds) {

        // 批量插入枚举选项信息
        if (!CollectionUtils.isEmpty(createEnumItems)) {
            extendFieldEnumItemDao.batchInsert(createEnumItems);
        }

        // 批量更新枚举选项信息
        if (!CollectionUtils.isEmpty(updateEnumItems)) {
            extendFieldEnumItemDao.batchUpdate(updateEnumItems);
            // 循环更新联系人枚举选项
        }

        // 批量删除枚举选项
        if (!CollectionUtils.isEmpty(deleteEnumItemIds)) {
            extendFieldEnumItemDao.batchDeleteByIds(deleteEnumItemIds);
            // 循环删除联系人枚举选项
        }
    }

    private ExtendFieldDO convertFrom(InsertFieldParam insertFieldParam) {
        ExtendFieldDO extendField = new ExtendFieldDO();
        extendField.setMemberType(insertFieldParam.getMemberTypeEnum().getIndex());
        extendField.setName(insertFieldParam.getName());
        extendField.setType(insertFieldParam.getExtendFieldTypeEnum().getIndex());
        return extendField;
    }

    private ExtendFieldDO convertFrom(UpdateFieldParam updateFieldParam) {
        ExtendFieldDO extendField = new ExtendFieldDO();
        extendField.setFieldId(updateFieldParam.getId());
        extendField.setName(updateFieldParam.getName());
        return extendField;
    }

    private ExtendFieldEnumItemDO convertFrom(InsertEnumItemParam insertEnumItemParam) {
        ExtendFieldEnumItemDO item = new ExtendFieldEnumItemDO();
        item.setName(insertEnumItemParam.getName());
        item.setSeq(insertEnumItemParam.getSerial());
        item.setDeleteStatus(1);
        item.setDisplayStatus(insertEnumItemParam.getDisplayStatus() ? 0 : 1);
        return item;
    }

    private ExtendFieldEnumItemDO convertFrom(UpdateEnumItemParam updateEnumItemParam) {
        ExtendFieldEnumItemDO item = new ExtendFieldEnumItemDO();
        item.setFieldEnumId(updateEnumItemParam.getId());
        item.setName(updateEnumItemParam.getName());
        item.setSeq(updateEnumItemParam.getSerial());
        if(updateEnumItemParam.getDisplayStatus() != null){
            item.setDisplayStatus(updateEnumItemParam.getDisplayStatus() ? 0 : 1);
        }
        return item;
    }

    private ExtendFieldDTO convertFrom(ExtendField extendField) {
        ExtendFieldDTO extendFieldDto = new ExtendFieldDTO();
        extendFieldDto.setId(extendField.getId());
        extendFieldDto.setName(extendField.getName());
        extendFieldDto.setType(extendField.getType());
        return extendFieldDto;
    }

    private ExtendFieldEnumItemDto convertFrom(ExtendFieldEnumItemDO enumItem){
        ExtendFieldEnumItemDto enumItemDto = ConvertUtil.extendFieldEnumItemDO2ExtendFieldEnumItemDto(enumItem);
        return enumItemDto;
    }
}

